﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Threading;
using System.Net;
using System.IO;
using System.Windows.Media.Imaging;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;

namespace ServerVideoChat
{
    [Serializable]
    public class IP_Camera : INotifyPropertyChanged, ISavable
    {
        #region Enums
        public enum Resolutions { half, full}
        #endregion

        public IP_Camera(string strIpAdress)
        {
            IpAdress = strIpAdress;
            Init();
        }
        public IP_Camera() { Init(); }

        #region Events
        public event EventHandler<NewImageEventArg> NewImage = delegate { };
        private void OnNewImage(byte[] btImage)
        {
            NewImage(this, new NewImageEventArg(btImage));
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };
        private void OnPropertyChanged(string strPropertyName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(strPropertyName));
        }

        #endregion

        #region Fields
        [NonSerialized]
        DispatcherTimer tmrGrabgingTimer = new DispatcherTimer();
        private int ErrorCounter = 0;
        #endregion

        #region Properties
        #region IpAdress
        private string strIpAdress;
        public string IpAdress
        {
            get { return strIpAdress; }
            set
            {
                if (strIpAdress == value) return;
                strIpAdress = value;
                OnPropertyChanged("IpAdress");
                UpdateUrl();
            }
        }
        #endregion

        #region IsAutoGrabbingMode
        private bool bIsAutoGrabbingMode;
        public bool IsAutoGrabbingMode
        {
            get { return bIsAutoGrabbingMode; }
            set
            {
                if (bIsAutoGrabbingMode == value && tmrGrabgingTimer.IsEnabled == value) return;
                bIsAutoGrabbingMode = value;
                OnPropertyChanged("IsAutoGrabbingMode");
                if (bIsAutoGrabbingMode) UpdateImage();
            }
        }
        #endregion

        #region IsAutoGrabbingMode
        private bool bShowIpFrame;
        public bool ShowIpFrame
        {
            get { return bShowIpFrame; }
            set 
            {
                bShowIpFrame = value;
                OnPropertyChanged("ShowIpFrame");
            }
        }
        #endregion

        #region AutoGrabbingTime
        private int tspAutoGrabbingTime = 10;
        public int AutoGrabbingTime
        {
            get { return tspAutoGrabbingTime; }
            set
            {
                if (tspAutoGrabbingTime == value) return;
                tspAutoGrabbingTime = value;
                tmrGrabgingTimer.Interval = TimeSpan.FromMilliseconds(tspAutoGrabbingTime);
                OnPropertyChanged("AutoGrabbingTime");
            }
        }
        #endregion

        #region Res
        private Resolutions res=Resolutions.full;
        public Resolutions Res
        {
            get { return res; }
            set
            {
                if (res == value) return;
                res = value;
                OnPropertyChanged("Res");
                UpdateUrl();
            }
        }
        #endregion

        #region X0
        private int x0;
        public int X0
        {
            get { return x0; }
            set
            {
                if (x0 == value) return;
                if (value < 0) throw new ArgumentException("Значение не может быть отрицательным!");
                x0 = value;
                OnPropertyChanged("X0");
                UpdateUrl();
            }
        }
        #endregion

        #region Y0
        private int yo;
        public int Y0
        {
            get { return yo; }
            set
            {
                if (yo == value) return;
                if (value < 0) throw new ArgumentException("Значение не может быть отрицательным!");
                yo = value;
                OnPropertyChanged("Y0");
                UpdateUrl();
            }
        }
        #endregion

        #region X1
        private int x1 = 800;
        public int X1
        {
            get { return x1; }
            set
            {
                if (x1 == value) return;
                if (value < 0) throw new ArgumentException("Значение не может быть отрицательным!");
                x1 = value;
                OnPropertyChanged("X1");
                UpdateUrl();
            }
        }
        #endregion

        #region Y1
        private int y1 = 600;
        public int Y1
        {
            get { return y1; }
            set
            {
                if (y1 == value) return;
                if (value < 0) throw new ArgumentException("Значение не может быть отрицательным!");
                y1 = value;
                OnPropertyChanged("Y1");
                UpdateUrl();
            }
        }
        #endregion

        #region Quality
        private int quality = 12;
        public int Quality
        {
            get { return quality; }
            set
            {
                if (quality == value) return;
                if (value < 1 || value > 20) throw new ArgumentException("Значение должно находиться в диапазоне от 1 до 20");
                quality = value;
                OnPropertyChanged("Quality");
                UpdateUrl();
            }
        }
        #endregion

        #region IsDoubleScan
        private bool bIsDoubleScan;
        public bool IsDoubleScan
        {
            get { return bIsDoubleScan; }
            set
            {
                if (bIsDoubleScan == value) return;
                bIsDoubleScan = value;
                OnPropertyChanged("IsDoubleScan");
                UpdateUrl();
            }
        }
        #endregion

        #region Url
        private string strUrl;
        public string Url
        {
            get { return strUrl; }
            private set
            {
                if (strUrl == value) return;
                strUrl = value;
                OnPropertyChanged("Url");
            }
        }
        #endregion

        #region ImageFrame
        [NonSerialized]
        private BitmapImage imageFrame;
        public BitmapImage ImageFrame
        {
            get { return imageFrame; }
            private set
            {
                if (imageFrame == value) return;
                imageFrame = value;
                OnPropertyChanged("ImageFrame");
            }
        }
        #endregion

        #endregion

        #region Methods
        public void GetFrame() 
        {
            if (IsAutoGrabbingMode) throw new InvalidOperationException("Функция GetFrame может использоваться только в случае IsAutoGrabbingMode=true");
            UpdateImage();
        }

        public void StartAutoGrabbing() { IsAutoGrabbingMode = true; }
        public void StopAutoGrabbing() { IsAutoGrabbingMode = false; }

        private void Init() 
        {
            //tmrGrabgingTimer.Tick += (_1, _2) => 
            //{
            //    tmrGrabgingTimer.Stop();
            //    UpdateImage();
            //    tmrGrabgingTimer.IsEnabled = IsAutoGrabbingMode;
            //};
        }
        
        private void UpdateUrl()
        {
            string strRezult = "http://" + IpAdress + "/image?res=" + Res.ToString() + "&x0=" + X0 + "&y0="+Y0+"&x1="+X1+"&y1="+Y1+"&quality="+Quality+"&doublescan=";
            strRezult += IsDoubleScan ? 1 : 0;
            Url = strRezult;
        }

        private void UpdateImage()
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(updateImage));
        }

        private void updateImage(object state)
        {
            byte[] buffer = new byte[10000000];

            while(IsAutoGrabbingMode)
            {
                int read, total = 0;
                HttpWebRequest req;
                try
                {
                    req = (HttpWebRequest)WebRequest.Create(Url);
                    // get response
                    WebResponse resp = req.GetResponse();

                    // get response stream
                    Stream stream = resp.GetResponseStream();

                    // read data from stream
                    while ((read = stream.Read(buffer, total, 1000)) != 0)
                    {
                        total += read;
                    }

                    byte[] btRezultBuffer = buffer.Take(total).ToArray();
                    //Console.WriteLine(total);
                    App.ViewModel.CurrentDisp.BeginInvoke((Action) (() =>
                    {
                        OnNewImage(btRezultBuffer);
                        if(bShowIpFrame)
                        {
                            MemoryStream ms = new MemoryStream(btRezultBuffer);
                            BitmapImage bmp = new BitmapImage();
                            bmp.BeginInit();
                            bmp.StreamSource = ms;
                            bmp.EndInit();
                            ImageFrame = bmp.Clone();
                        }
                    }));
                    
                }
                catch (UriFormatException ex)
                {
                    App.Log.Error("Ошибка при вводе URL IP-камеры", ex);
                    if(ErrorCounter++ > 5)
                    {
                        App.ViewModel.AddLogMessage("Ошибка при вводе URL IP-камеры. Опрос IP-камеры остановлен");
                        StopAutoGrabbing();
                    }
                    App.ViewModel.AddLogMessage("Ошибка при вводе URL IP-камеры. Повторный запрос...");

                }
                catch (WebException ex)
                {
                    App.Log.Error("Ошибка при вводе URL IP-камеры", ex);
                    if (ErrorCounter++ > 5)
                    {
                        App.ViewModel.AddLogMessage("Ошибка при вводе URL IP-камеры. Опрос IP-камеры остановлен");
                        StopAutoGrabbing();
                    }
                    App.ViewModel.AddLogMessage("Ошибка при вводе URL IP-камеры. Повторный запрос...");
                }
                catch (Exception ex)
                {
                    App.Log.Error("Неидентифицированная ошибка в UpdateImage", ex);
                    if (ErrorCounter++ > 5)
                    {
                        App.ViewModel.AddLogMessage("Неидентифицированная ошибка в UpdateImage. Опрос IP-камеры остановлен");
                        StopAutoGrabbing();
                    }
                    App.ViewModel.AddLogMessage("Неидентифицированная ошибка в UpdateImage. Повторный запрос...");
                }
                finally
                {
                    Thread.Sleep(AutoGrabbingTime);
                }
            }
           
            
        }
        #endregion

        #region ISavable Members
        public byte[] GetSettings()
        {
            byte[] Rezult;
            Dictionary<string, object> dicSettings = new Dictionary<string, object>();
            dicSettings.Add("IpAdress", IpAdress);
            dicSettings.Add("IsAutoGrabbingMode", IsAutoGrabbingMode);
            dicSettings.Add("AutoGrabbingTime", AutoGrabbingTime);
            dicSettings.Add("Res", Res);
            dicSettings.Add("X0", X0);
            dicSettings.Add("X1", X1);
            dicSettings.Add("Y0", Y0);
            dicSettings.Add("Y1", Y1);
            dicSettings.Add("Quality", Quality);
            dicSettings.Add("IsDoubleScan", IsDoubleScan);
            using (MemoryStream ms = new MemoryStream())
            {
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(ms, dicSettings);
                Rezult = ms.GetBuffer();
            }
            return Rezult;
        }
        public void SetSettings(byte[] btSetings)
        {
            Dictionary<string, object> dicSettings;
            using (MemoryStream ms = new MemoryStream(btSetings))
            {
                BinaryFormatter bf = new BinaryFormatter();
                dicSettings = bf.Deserialize(ms) as Dictionary<string, object>;
            }
            try
            {


            IpAdress = dicSettings.Where(p => p.Key == "IpAdress").Single().Value as string;
            IsAutoGrabbingMode = (bool)dicSettings.Where(p => p.Key == "IsAutoGrabbingMode").Single().Value;
                try
                {
                    AutoGrabbingTime = (int)dicSettings.Where(p => p.Key == "AutoGrabbingTime").Single().Value;

                }
                catch (Exception)
                {
                
                }
            Res = (Resolutions)dicSettings.Where(p => p.Key == "Res").Single().Value;
            X0 = (int)dicSettings.Where(p => p.Key == "X0").Single().Value;
            X1 = (int)dicSettings.Where(p => p.Key == "X1").Single().Value;
            Y0 = (int)dicSettings.Where(p => p.Key == "Y0").Single().Value;
            Y1 = (int)dicSettings.Where(p => p.Key == "Y1").Single().Value;
            Quality = (int)dicSettings.Where(p => p.Key == "Quality").Single().Value;
            IsDoubleScan = (bool)dicSettings.Where(p => p.Key == "IsDoubleScan").Single().Value;
            }
            catch (Exception)
            {

            }
        }
        #endregion
    }

    [Serializable]
    public class NewImageEventArg : EventArgs
	{
        public NewImageEventArg(byte[] btImageBuffer)
        {
            imageBuffer = btImageBuffer.Clone() as byte[];
        }
        
        #region ImageBuffer
      private byte[] imageBuffer;
	  public byte[] ImageBuffer
	  {
          get { return imageBuffer;}
	  }
      #endregion
	}
}
